// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types2

import (
	"cmd/compile/internal/syntax"
	"sync"
)

// Named代表一个已命名（已定义）的类型。
type Named struct {
	check      *Checker
	obj        *TypeName      // 声明类型对应的声明对象；实例化类型的占位符
	orig       *Named         // 原始、非实例化类型
	fromRHS    Type           // 类型（在声明的右侧）此*命名类型派生自（用于周期报告）
	underlying Type           // 可能是在设置期间命名的*；从来没有一个*Named once完全设置
	tparams    *TypeParamList // 类型参数，或nil 
	targs      *TypeList      // 类型参数（实例化后），或nil 

	// 为此类型声明的方法（不是此类型的方法集）。
	// 签名被延迟地进行类型检查。
	// 对于非实例化类型，这是一个完全填充的方法列表。对于
	// 实例化的类型，这是一个“惰性”列表，方法在第一次访问时被实例化。
	methods *methodList

	// 可以提供解析器来延迟解析类型参数、基础参数和方法。
	resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList)
	once     sync.Once // 确保在访问
}

// NewNamed为给定的类型名、基础类型和关联方法返回一个新的命名类型之前，先解析Tparam、基础类型和方法。
// 如果给定的类型名obj还没有类型，则其类型将设置为返回的命名类型。
// 基础类型不能是*命名的。
func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	return (*Checker)(nil).newNamed(obj, nil, underlying, nil, newMethodList(methods))
}

func (t *Named) resolve(ctxt *Context) *Named {
	if t.resolver == nil {
		return t
	}

	t.once.Do(func() {
		// TODO（mdempsky）：既然我们无论如何都要把t传递给解析器
		// （这是必需的，因为types2希望定义接口类型上的方法
		// 的接收方类型是命名的，而不是
		// 基础接口），也许它应该只处理调用
		// SetTypeParams，setUndergrounding，而不是一种方法？不过，那些
		// 方法需要支持可重入调用。这将使API在未来的扩展中更加可靠（比如SetTypeParams）。
		t.tparams, t.underlying, t.methods = t.resolver(ctxt, t)
		t.fromRHS = t.underlying // 用于周期检测
	})
	return t
}

// newNamed与newNamed类似，但带有*检查接收器和附加的orig参数。
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams *TypeParamList, methods *methodList) *Named {
	typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
	if typ.orig == nil {
		typ.orig = typ
	}
	if obj.typ == nil {
		obj.typ = typ
	}
	// 确保始终扩展类型并检查其是否正常。
	if check != nil {
		check.needsCleanup(typ)
	}
	return typ
}

func (t *Named) cleanup() {
	// 确保在类型检查过程中创建的每个已定义类型都有
	// 未命名的基础类型或未解析的类型。
	// 
	// 这保证了我们不会泄漏任何底层为*命名的类型，因为任何未解析的实例都会通过替换其源的底层来懒洋洋地计算其底层。原点必须有
	// 已导入或在此处进行了类型检查和扩展，在任何情况下，
	// 其基础将被完全扩展。
	switch t.underlying.(type) {
	case nil:
		if t.resolver == nil {
			panic("nil underlying")
		}
	case *Named:
		t.under() // t.under可以添加要检查的条目。
	}
	t.check = nil
}

// Obj返回定义命名类型t的声明的类型名。对于
// 实例化类型，这与源类型的类型名相同。
func (t *Named) Obj() *TypeName { return t.orig.obj } // 对于非实例，这与t.obj 

// Origin返回从中实例化命名类型t的泛型类型。如果t不是实例化类型，结果就是t。
func (t *Named) Origin() *Named { return t.orig }

// TODO（gri）提出了一个更好的表示和API来区分参数化实例化类型和非实例化类型。

// TypeParams返回命名类型t的类型参数，或nil。
// 对于一个（最初的）泛型类型，即使它被实例化，结果也是非零的。
func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }

// SetTypeParams设置命名类型t的类型参数。
// t不能有类型参数。
func (t *Named) SetTypeParams(tparams []*TypeParam) {
	assert(t.targs.Len() == 0)
	t.resolve(nil).tparams = bindTParams(tparams)
}

// TypeArgs返回用于实例化命名类型t的类型参数。
func (t *Named) TypeArgs() *TypeList { return t.targs }

// NumMethods返回为t定义的显式方法数。
// 
// 对于普通或实例化类型t，这些
// 方法的接收器基类型将是命名类型t。对于未实例化的泛型t，每个
// 方法接收器将使用其接收器类型参数进行实例化。
func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }

// 方法返回0<=i<t.NumMethods（）的命名类型为t的第i个方法。
func (t *Named) Method(i int) *Func {
	t.resolve(nil)
	return t.methods.At(i, func() *Func {
		return t.instantiateMethod(i)
	})
}

// InstitateMethod为实例化的接收方实例化第i个方法。
func (t *Named) instantiateMethod(i int) *Func {
	assert(t.TypeArgs().Len() > 0) // t必须是实例

	// t.orig。方法并不懒惰。origm是用其
	// 接收器类型参数实例化的方法（“origin”方法）。
	origm := t.orig.Method(i)
	assert(origm != nil)

	check := t.check
	// 确保对原始方法进行了类型检查。
	if check != nil {
		check.objDecl(origm, nil)
	}

	origSig := origm.typ.(*Signature)
	rbase, _ := deref(origSig.Recv().Type())

	// 如果rbase是t，那么origm已经是我们正在寻找的实例化方法
	// 了。在这种情况下，我们返回origm以保持不变，即
	// 遍历方法->接收器类型->方法应该返回到相同的
	// 方法。
	// 
	// 如果用receiver type参数实例化t，就会发生这种情况，如在
	// func（rt[]）m（）{r.m（）}中使用m。
	if rbase == t {
		return origm
	}

	sig := origSig
	// 只有当类型参数
	// 和类型参数之间存在对应关系时，我们才能替换。如果
	// 代码无效，则需要进行此检查。
	if origSig.RecvTypeParams().Len() == t.targs.Len() {
		ctxt := check.bestContext(nil)
		smap := makeSubstMap(origSig.RecvTypeParams().list(), t.targs.list())
		sig = check.subst(origm.pos, origSig, smap, ctxt).(*Signature)
	}

	if sig == origSig {
		// 没有发生替换，但是我们仍然需要创建一个新的签名来保存实例化的接收方。
		copy := *origSig
		sig = &copy
	}

	var rtyp Type
	if origm.hasPtrRecv() {
		rtyp = NewPointer(t)
	} else {
		rtyp = t
	}

	sig.recv = substVar(origSig.recv, rtyp)
	return NewFunc(origm.pos, origm.pkg, origm.name, sig)
}

// setUnderground设置基础类型并将t标记为完整。
// t不能有类型参数。
func (t *Named) SetUnderlying(underlying Type) {
	assert(t.targs.Len() == 0)
	if underlying == nil {
		panic("underlying type must not be nil")
	}
	if _, ok := underlying.(*Named); ok {
		panic("underlying type must not be *Named")
	}
	t.resolve(nil).underlying = underlying
	if t.fromRHS == nil {
		t.fromRHS = underlying // 对于周期检测
	}
}

// AddMethod添加方法m，除非它已经在方法列表中。
// t不能有类型参数。
func (t *Named) AddMethod(m *Func) {
	assert(t.targs.Len() == 0)
	t.resolve(nil)
	if t.methods == nil {
		t.methods = newMethodList(nil)
	}
	t.methods.Add(m)
}

func (t *Named) Underlying() Type { return t.resolve(nil).underlying }
func (t *Named) String() string   { return TypeString(t, nil) }

// ---------------------------------------------------------------
// 实现

// 在下面返回n0的扩展基础类型；可能是通过遵循
// 命名类型的前向链。如果找到基础类型，在返回之前，通过为
// 链。如果未找到基础类型或检测到循环
// 链中定义的每个类型设置基础类型来解析
// ，则结果为Typ[无效]。如果检测到循环，
// n0。检查！=无，周期报告。
// 
// 这是必要的，因为命名的基础类型本身可能是一个不完整的命名类型：
// 
// type（
// a B 
// B*C 
// C a 
// ）
// 并且其基础类型是命名的类型B。
func (n0 *Named) under() Type {
	u := n0.Underlying()

	// 如果定义类型的基础类型不是定义的
	// （包括实例）类型，那么这就是所需的基础
	// 类型。
	var n1 *Named
	switch u1 := u.(type) {
	case nil:
		// 通过基础（）扩展后，我们永远不会遇到零
		// 基础。
		panic("nil underlying")
	default:
		// 常见情况
		return u
	case *Named:
		// 在下面处理
		n1 = u1
	}

	if n0.check == nil {
		panic("Named.check == nil but type is incomplete")
	}

	// 不变量：在这一点之后，n0及其
	// 中的任何命名类型都应在该函数退出时设置基础链。
	check := n0.check
	n := n0

	seen := make(map[*Named]int) // 需要其基础解析的类型
	var path []Object            // 遇到的对象，用于循环报告

loop:
	for {
		seen[n] = len(seen)
		path = append(path, n.obj)
		n = n1
		if i, ok := seen[n]; ok {
			// 循环
			check.cycleError(path[i:])
			u = Typ[Invalid]
			break
		}
		u = n.Underlying()
		switch u1 := u.(type) {
		case nil:
			u = Typ[Invalid]
			break loop
		default:
			break loop
		case *Named:
			// 继续收集链中的*命名类型。
			n1 = u1
		}
	}

	for n := range seen {
		// 我们永远不必更新导入类型的基础类型；
		// 这些基础类型应该在导入期间得到解析。
		// 同样，这样做会导致种族状况（was问题#31749）。
		// 始终执行此检查，而不仅仅是在调试模式下（这很便宜）。
		if n.obj.pkg != check.pkg {
			panic("imported type with unresolved underlying type")
		}
		n.underlying = u
	}

	return u
}

func (n *Named) setUnderlying(typ Type) {
	if n != nil {
		n.underlying = typ
	}
}

func (n *Named) lookupMethod(pkg *Package, name string, foldCase bool) (int, *Func) {
	n.resolve(nil)
	// 如果n是一个实例，我们可能还没有实例化它的所有方法。
	// 在orig中查找方法索引，仅在
	// 匹配索引（如果有）处实例化方法。
	i, _ := n.orig.methods.Lookup(pkg, name, foldCase)
	if i < 0 {
		return -1, nil
	}
	// 例如，m.Method（i）将不同于orig方法。
	return i, n.Method(i)
}

// bestContext返回可用的最佳上下文。按优先顺序：
// /-给定的ctxt，如果非零
// /-检查。ctxt，如果check为non-nil 
// -一个新的上下文
func (check *Checker) bestContext(ctxt *Context) *Context {
	if ctxt != nil {
		return ctxt
	}
	if check != nil {
		if check.ctxt == nil {
			check.ctxt = NewContext()
		}
		return check.ctxt
	}
	return NewContext()
}

// expandNamed确保n的底层类型被实例化。
// 如果出现错误，则基础类型将是Typ[Invalid]。
func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypeParamList, underlying Type, methods *methodList) {
	n.orig.resolve(ctxt)
	assert(n.orig.underlying != nil)

	check := n.check

	if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
		// 在类型检查期间，我们应该只在这里获得未展开的底层
		// （例如，在递归类型声明中）。
		assert(check != nil)
	}

	// 可以在其他地方检查arg和tparam长度不匹配的情况。
	if n.orig.tparams.Len() == n.targs.Len() {
		// 我们必须始终有一个上下文，以避免无限递归。
		ctxt = check.bestContext(ctxt)
		h := ctxt.instanceHash(n.orig, n.targs.list())
		// 确保为h记录一个实例，以避免无限递归。
		ctxt.update(h, n.orig, n.TypeArgs().list(), n)

		smap := makeSubstMap(n.orig.tparams.list(), n.targs.list())
		underlying = n.check.subst(instPos, n.orig.underlying, smap, ctxt)
		// 如果n的底层是一个接口，我们需要准确地设置
		// 其方法的接收方——我们在
		// 上设置接口方法的接收方，将类型声明的RHS设置为定义的类型。
		if iface, _ := underlying.(*Interface); iface != nil {
			if methods, copied := replaceRecvType(iface.methods, n.orig, n); copied {
				// 如果底层没有实际使用类型参数，那么它可能没有被替换。在这种情况下，我们需要创建一个新的
				if iface == n.orig.underlying {
					old := iface
					iface = check.newInterface()
					iface.embeddeds = old.embeddeds
					iface.complete = old.complete
					iface.implicit = old.implicit // 应该为false，但要保守
					underlying = iface
				}
				iface.methods = methods
			}
		}
	} else {
		underlying = Typ[Invalid]
	}

	return n.orig.tparams, underlying, newLazyMethodList(n.orig.methods.Len())
}

// 安全基础返回类型的基础，而不扩展实例，以
// 避免无限递归。
// 
// TODO（rfindley）：删除此函数或为其命名。
func safeUnderlying(typ Type) Type {
	if t, _ := typ.(*Named); t != nil {
		return t.underlying
	}
	return typ.Underlying()
}
